#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
#include "videoplayer.h"


SDL_Surface *screen;
static struct SwsContext *img_convert_ctx; 
VideoState *global_video_state;

int decode_interrupt_cb(void){
	return (global_video_state && global_video_state -> quit);
}



int queue_picture(VideoState *is, AVFrame *pFrame){
	VideoPicture *vp;
	int dst_pix_fmt;
	AVPicture pict;

	SDL_LockMutex(is -> pictq_mutex);
	while(is -> pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is -> quit){
		SDL_CondWait(is -> pictq_cond, is -> pictq_mutex);
	}
	SDL_UnlockMutex(is -> pictq_mutex);

	if(is -> quit)
		return -1;

	vp = &is -> pictq[is -> pictq_windex];

	if(!vp -> bmp ||
	   vp -> width != is -> video_st -> codec -> width ||
	   vp -> height != is -> video_st -> codec -> height){
		SDL_Event event;
		
		vp -> allocated = 0;	
		event.type = FF_ALLOC_EVENT;
		event.user.data1 = is;
		SDL_PushEvent(&event);

		SDL_LockMutex(is -> pictq_mutex);
		while(!vp -> allocated && !is -> quit){
			SDL_CondWait(is -> pictq_cond, is -> pictq_mutex);
		}
		SDL_UnlockMutex(is->pictq_mutex);
		if(is -> quit){
			return -1;
		}
	
	}

	if(vp -> bmp){
		SDL_LockYUVOverlay(vp -> bmp);
		dst_pix_fmt = PIX_FMT_YUV420P;

		pict.data[0] = vp -> bmp -> pixels[0];
		pict.data[1] = vp -> bmp -> pixels[2];
		pict.data[2] = vp -> bmp -> pixels[1];

		pict.linesize[0] = vp -> bmp -> pitches[0];
		pict.linesize[1] = vp -> bmp -> pitches[2];
		pict.linesize[2] = vp -> bmp -> pitches[1];
		
//		img_convert()
		img_convert_ctx = sws_getContext(is -> video_st -> codec -> width, is -> video_st ->codec -> height, is -> video_st -> codec -> pix_fmt, is -> video_st -> codec -> width, is ->video_st -> codec -> height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
		sws_scale(img_convert_ctx, (AVPicture *)pFrame, (AVPicture *)pFrame -> linesize, 0, is->video_st -> codec -> height, pict.data, pict.linesize);

		SDL_UnlockYUVOverlay(vp -> bmp);

		if(++ is ->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE){
			is -> pictq_windex = 0;
		}

		is -> pictq_size ++;
		SDL_UnlockMutex(is -> pictq_mutex);
	}

	return 0;
}



int video_thread(void *arg){
	

	VideoState *is = (VideoState *) arg;
	AVPacket pkt1,*packet;
	packet = &pkt1;
	int len1, frameFinished;
	AVFrame *pFrame;

	pFrame = avcodec_alloc_frame();

	printf("debug\n");

	for(;;){
		if(packet_queue_get(&is->videoq,packet,1) < 0){
			
printf("video_thread debug\n");	
			break;
		}

		len1 = avcodec_decode_video2(is->video_st->codec,pFrame,&frameFinished, packet);
		if(frameFinished){
			if(queue_picture(is,pFrame) < 0){
				break;
			}
		}

		av_free_packet(packet);
	}
	av_free(pFrame);
	return 0;
}

int decode_thread(void *arg){

	printf("decode_thread\n");

	VideoState *is = (VideoState*)arg;
	AVFormatContext *pFormatCtx = NULL;
	AVPacket pkt1, *packet = &pkt1;


	int video_index = -1;
	int audio_index = -1;
	int i;

	is -> videoStream = -1;
	is -> audioStream = -1;


	global_video_state = is;
	url_set_interrupt_cb(decode_interrupt_cb);



	if(avformat_open_input(&pFormatCtx,is->filename,NULL,NULL) != 0){
		return -1;
	}


	is -> pFormatCtx = pFormatCtx;


	if(av_find_stream_info(pFormatCtx) < 0)
		return -1;

	
	av_dump_format(pFormatCtx,0,is->filename,0);


	for(i = 0; i<pFormatCtx->nb_streams; i++){
		if(pFormatCtx -> streams[i] -> codec -> codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0){
			video_index = i;
		}

		if(pFormatCtx -> streams[i] -> codec -> codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0){
			audio_index = i;
		}
		
	}

	is -> audioStream = audio_index;
	is -> videoStream = video_index;


	if(audio_index >= 0)
		stream_component_open(is,audio_index);
	if(video_index >= 0)
		stream_component_open(is,video_index);
	
	printf("audiostream:%d,videostream:%d",audio_index,video_index);

	if(is -> videoStream < 0 || is -> audioStream < 0){
		fprintf(stderr,"%s:could not open codec\n",is -> filename);
		goto fail;
	}


	for(;;){
		if(is->quit){
			break;
		}

		if(is->audioq.size > MAX_AUDIOQ_SIZE ||
		   is->videoq.size > MAX_VIDEOQ_SIZE){
			SDL_Delay(10);
			continue;
		}

		if(av_read_frame(is->pFormatCtx, packet) < 0){
			if((pFormatCtx -> pb -> error) == 0){

				SDL_Delay(100);
				continue;
			} else {
				break;
			}
		}

		if(packet -> stream_index == is->videoStream){
			packet_queue_put(&is->videoq,packet);
		} else if(packet -> stream_index == is->audioStream){
			packet_queue_put(&is->audioq,packet);
		} else{
			av_free_packet(packet);
		}
	}

	while(!is->quit){
		SDL_Delay(100);
	}

	fail:
	if(1){
		SDL_Event event;
		event.type = FF_QUIT_EVENT;
		event.user.data1 = is;
		SDL_PushEvent(&event);
	}
	return 0;
}

void packet_queue_init(PacketQueue *q){
	memset(q, 0, sizeof(PacketQueue));
	q -> mutex = SDL_CreateMutex();
	q -> cond = SDL_CreateCond();
}

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block){
	AVPacketList *pkt1;
	int ret;

	SDL_LockMutex(q -> mutex);

	for(;;){
		if(global_video_state -> quit){
			ret = -1;
			break;
		}

		pkt1 = q->first_pkt;
		if(pkt1){
			q -> first_pkt = pkt1 -> next;
			q -> nb_packets--;
			q -> size -= pkt1->pkt.size;
			*pkt = pkt1 -> pkt;
			av_free(pkt1);
			ret = 1;
			break;
		} else if(!block){
			ret = 0;
			break;
		} else {
			SDL_CondWait(q->cond,q->mutex);
		}
	}
	SDL_UnlockMutex(q->mutex);
	return ret;
}

int packet_queue_put(PacketQueue *q, AVPacket *pkt){
	AVPacketList *pkt1;
	if(av_dup_packet(pkt) < 0){
		return -1;
	}
	pkt1 = av_malloc(sizeof(AVPacketList));
	if(!pkt1)
		return -1;
	pkt1 -> pkt = *pkt;
	pkt1 -> next = NULL;

	SDL_LockMutex(q -> mutex);

	if(!q -> last_pkt)
		q -> first_pkt = pkt1;
	else
		q -> last_pkt -> next = pkt1;

	q -> last_pkt = pkt1;
	q -> nb_packets++;

	q -> size += pkt1 -> pkt.size;
	SDL_CondSignal(q -> cond);

	SDL_UnlockMutex(q -> mutex);
	return 0;
}

static void schedule_refresh(VideoState *is, int delay){
	SDL_AddTimer(delay, sdl_refresh_timer_cb,is);
}

static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque){
	SDL_Event event;
	event.type = FF_REFRESH_EVENT;
	event.user.data1 = opaque;
	SDL_PushEvent(&event);
	return 0;
}

void video_display(VideoState *is){
	SDL_Rect rect;
	VideoPicture *vp;
	AVPicture pict;
	float aspect_ratio;
	int w, h, x, y;
	int i;

	vp = &is -> pictq[is->pictq_rindex];
	if(vp -> bmp){
		if(is -> video_st -> codec -> sample_aspect_ratio.num == 0){
			aspect_ratio = 0;
		} else {
			aspect_ratio = av_q2d(is -> video_st -> codec -> sample_aspect_ratio) * (is -> video_st -> codec -> width) / (is -> video_st -> codec -> height);
		}

		if(aspect_ratio <= 0.0){
			aspect_ratio = (float)(is -> video_st -> codec -> width) / (float)(is -> video_st -> codec -> height);
		}

		h = screen -> h;
		w = ((int)rint(h * aspect_ratio))& -3;

		if(w > screen -> w){
			w = screen -> w;
			h = ((int)rint(w/aspect_ratio)) & -3;
		}

		x = (screen -> w - w) / 2;
		y = (screen -> h - h) / 2;

		rect.x = x;
		rect.y = y;
		rect.w = w;
		rect.h = h;  

		SDL_DisplayYUVOverlay(vp -> bmp, &rect);
	}
}

int audio_decode_frame(VideoState *is, uint8_t *audio_buf, int buf_size){
	int len1, data_size;
	AVPacket *pkt = &is -> audio_pkt;
	
	for(;;){
		while(is -> audio_pkt_size > 0){
			data_size = buf_size;
			len1 = avcodec_decode_audio3(is -> audio_st -> codec,
						     (int16_t *)audio_buf, &data_size,
						     pkt);
			if(len1 < 0){
				is -> audio_pkt_size = 0;
				break;
				}
			is -> audio_pkt_data += len1;
			is -> audio_pkt_size -= len1;

			if(data_size <= 0){
				continue;
			}
			return data_size;
		}
		if(pkt -> data)
			av_free_packet(pkt);
		if(is -> quit){
			return -1;
		}

		if(packet_queue_get(&is -> audioq, pkt, 1) < 0){
			return -1;
		}
		is -> audio_pkt_data = pkt -> data;
		is -> audio_pkt_size = pkt -> size;
	}
}

int audio_callback(void *userdata, Uint8 *stream, int len){
	VideoState *is = (VideoState *)userdata;
	int len1, audio_size;


	while(len > 0){
		if(is -> audio_buf_index >= is -> audio_buf_size){
			audio_size = audio_decode_frame(is, is->audio_buf, sizeof(is ->audio_buf));
			if(audio_size < 0){
				is -> audio_buf_size = 1024;
				memset(is -> audio_buf, 0, is -> audio_buf_size);
			} else {
				is -> audio_buf_size = audio_size;
			}
			is->audio_buf_index = 0;
		}
		len1 = is -> audio_buf_size - is -> audio_buf_index;
		if(len1 > len);
		memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
		len -= len1;
		stream += len1;
		is -> audio_buf_index += len1;
	}
}




void video_refresh_timer(void *userdata){
	VideoState *is = (VideoState *) userdata;
	VideoPicture *vp;

	if(is -> video_st){
		if(is ->pictq_size == 0){
			schedule_refresh(is,1);
		} else{
			vp = &is -> pictq[is->pictq_rindex];
			schedule_refresh(is,80);
			video_display(is);

			if(++is -> pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE){
				is -> pictq_rindex = 0;
			}

			SDL_LockMutex(is -> pictq_mutex);
			is -> pictq_size-- ;
			SDL_CondSignal(is -> pictq_cond);
			SDL_UnlockMutex(is -> pictq_mutex);
		}
	} else {
		schedule_refresh(is,100);
	}
}
void alloc_picture(void *userdata){
	VideoState *is = (VideoState *) userdata;
	VideoPicture *vp;

	vp = &is -> pictq[is ->pictq_windex];
	if(vp -> bmp){
		SDL_FreeYUVOverlay(vp -> bmp);
	}

	vp -> bmp = SDL_CreateYUVOverlay(is -> video_st -> codec -> width,
					 is -> video_st -> codec -> height,
					 SDL_YV12_OVERLAY,
					 screen);

	vp -> width = is -> video_st -> codec -> width;
	vp -> height = is -> video_st -> codec -> height;

	SDL_LockMutex(is -> pictq_mutex);
	vp -> allocated = 1;
	SDL_CondSignal(is -> pictq_cond);
	SDL_UnlockMutex(is -> pictq_mutex);
	
}




int stream_component_open(VideoState *is, int stream_index){
	VideoState *arg = is;
	AVFormatContext *pFormatCtx = is -> pFormatCtx;
	AVCodecContext * codecCtx;
	AVCodec *codec;

	SDL_AudioSpec wanted_spec, spec;

	if(stream_index < 0 || stream_index >= pFormatCtx -> nb_streams){
		return -1;
	}

	codecCtx = pFormatCtx -> streams[stream_index] -> codec;

	if(codecCtx ->codec_type == AVMEDIA_TYPE_AUDIO){
		wanted_spec.freq = codecCtx->sample_rate;
	        wanted_spec.format = AUDIO_S16SYS;
	        wanted_spec.channels = codecCtx -> channels;
        	wanted_spec.silence = 0;
	        wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
        	wanted_spec.callback = audio_callback;
	        wanted_spec.userdata = is;

		if(SDL_OpenAudio(&wanted_spec,&spec) < 0){
			fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
			return -1;
		}
	}

	codec = avcodec_find_decoder(codecCtx -> codec_id);

	if(!codec || (avcodec_open(codecCtx,codec) < 0)){
		fprintf(stderr, "Unsupported codec!\n");
		return -1;
	}


	switch(codecCtx->codec_type){
	case AVMEDIA_TYPE_AUDIO:

		is->audio_st = pFormatCtx->streams[stream_index];
		is->audio_buf_size = 0;
		is->audio_buf_index = 0;
		memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
		packet_queue_init(&is->audioq);
		SDL_PauseAudio(0);
		break;
	case AVMEDIA_TYPE_VIDEO:
		is->videoStream = stream_index;
		
printf("is->videoStream = %d,stream_index = %d",is->videoStream,stream_index);

		is->video_st = pFormatCtx -> streams[stream_index];

		packet_queue_init(&is->videoq);
		is->video_tid = SDL_CreateThread(video_thread, arg);
		break;
	default:
		break;
	}
}



int main(int argc, char *argv[]){

	SDL_Event	event;

	VideoState	*is;

	av_register_all();

	is = av_mallocz(sizeof(VideoState));

	av_strlcpy(is->filename,argv[1],sizeof(is->filename));

	is->pictq_mutex = SDL_CreateMutex();
	is->pictq_cond = SDL_CreateCond();

	schedule_refresh(is,40);

	is->parse_tid = SDL_CreateThread(decode_thread, is);
	

	if(!is->parse_tid){
		printf("fail");
		av_free(is);
		return -1;
	}


	printf("FF_ALLOC_EVENT:%d\n",FF_ALLOC_EVENT);
	printf("FF_REFRESH_EVENT:%d\n",FF_REFRESH_EVENT);
	printf("FF_QUIT_EVENT:%d\n",FF_QUIT_EVENT);

	
	for(;;){
		SDL_WaitEvent(&event);


		switch(event.type){
			case FF_QUIT_EVENT:
			case SDL_QUIT:
				is -> quit = 1;
				SDL_Quit();
				return 0;
				break;
			case FF_ALLOC_EVENT:
				alloc_picture(event.user.data1);
				break;
			case FF_REFRESH_EVENT:
				video_refresh_timer(event.user.data1);
				break;
			default:
				break;
		}
	}
	return 0;
}
